/*==========================================================================
 *
 *  Copyright (C) 1995 Microsoft Corporation. All Rights Reserved.
 *	Source		:	DSstream.cpp
 *  File		:	DsApi.cpp
 *	Edited by	:	Liu Gang
 *	Version		:	V0023
 *	V0010		:	Oct.8.1996
 *	V0011		:	Nov.25.1996
 *	V0012		:	Apr.1.1997, add IfPlaying() function to classes
 *	V0020		:	Apr.2.1997, fixed a bug when playing static sound, 
 *								do not use Mutex with static wave objects
 *	V0021		:	May.2.1997, add function DS_IfSoundEnable()
 *	V0022		:	May.10.1997, fixed a bug in IfPlaying(), close the sound if the sound is end
 *	V0023		:	May.15.1997, fixed bugs when quit and init sound frequently
 *	V0024		:	Aug.13.1997, fixed a bug when playing
 *  Content:   streams data from a disk WAVE file to a
 *             DirectSound secondary buffer for playback.
 *
 ***************************************************************************/

#include "stdafx.h"
#include "dsApi.h"
#include "dsWave.h"	// wave file functions
#include "Assert.h"
#include <dsound.h>

//-----debug
//#define	_DS_LOG_
#ifdef	_DS_LOG_
	#include <stdio.h>
#endif
//-----debug
#define	_TEST_SOUND_

#ifndef DSBCAPS_CTRLDEFAULT
#define DSBCAPS_CTRLDEFAULT (DSBCAPS_CTRLFREQUENCY | DSBCAPS_CTRLPAN | DSBCAPS_CTRLVOLUME)
#endif

// globals for controling sounds
/////////////
// global sound device
LPDIRECTSOUND           lpDS = NULL;

// global dynamic waveinfo objects
int				nWaveCounter=0;		// counter for them
LPWAVEDYNAMIC	lpwdWaves[WAVE_OBJECT_MAX];

// global static waveinfo objects
int				nSWaveCounter=0;	// counter for them
LPWAVESTATIC	lpwsWaves[WAVE_OBJECT_MAXS];

// handle of window to receive error messages
HWND	DS_hwndGame;
/////////////

// globals for wave sound
/////////////
// true for enable sound
BOOL DS_bSoundEnable = FALSE;

// true for sound is usable
BOOL DS_bInitialized = FALSE;

// fade mode varible
int	DS_nFadeMode = DS_FADE_NONE;
// sound volume	varible
int	DS_nVolume = 0;
// sound pan varible
int DS_nPan = 0;
/////////////

// multi threads
/////////////
// used by TimeFunc() and Stop() in WAVEDYNAMIC
// not used in WAVESTATIC
// WaitforSingleObject() to get mutex
// ReleaseMutex() to release mutex
BOOL bCreateMutex = FALSE;
HANDLE	hMutex=0;
CRITICAL_SECTION	csPlayKey; // Guards one variable, gbPlay
/////////////


// initialize sound
/////////////
// error handle
// hwnd		:	error window
// err		:	error number
// return value	:	Always false
BOOL SoundFail( HWND hWnd, int err );

// for static wave only
// called if the static sound buffer got the end
// clear all the sounds played prviously, and now they are stoped.
void DS_CloseStopedSoundS();
/////////////

/*****************************************************************************/
/* DS_InitSound()                                                            */
/*                                                                           */
/* Performs sound initialization.											 */
/*                                                                           */
/*****************************************************************************/
LPDIRECTSOUNDBUFFER	lpDSBPrimary = NULL;
WAVEFORMATEX wfx;
// initialize direct sound object
// hwnd		:	handle of window to receive error messages
// nType		:	wave format, 
//					default if do not want to change params of waves
// return value	:	TRUE if succeeded
BOOL DS_InitSound( HWND hwnd, int nType/* = DS_WAVE_FORMAT_AUTO*/ )
{
    HRESULT         hr;
	DSCAPS			dscaps;
    DSBUFFERDESC    dsbd;

	if( DS_bInitialized == TRUE )
	{
		OutputDebugString( "DS_InitSound Warning: sound has been initialized!\n" );
		return FALSE;	// sound has been initialized
	}

	DS_hwndGame = hwnd;

    hr = DirectSoundCreate( NULL, &lpDS, NULL );
    if( hr != DS_OK )
    {
		return SoundFail( DS_hwndGame, 0 );
    }

	if( nType == DS_WAVE_FORMAT_AUTO )
	{
	    // SetCooperativeLevel, we only need normal mode
		if(( hr = lpDS->SetCooperativeLevel( DS_hwndGame, DSSCL_NORMAL )) != DS_OK )
		{
			return SoundFail( DS_hwndGame, 1 );
		}
	}
	else
	{
	    // SetCooperativeLevel, normal mode is not enough
		if(( hr = lpDS->SetCooperativeLevel( DS_hwndGame, DSSCL_PRIORITY )) != DS_OK )
		{
			return SoundFail( DS_hwndGame, 4 );
		}
	}
	

	// get sound card compatible configration
	dscaps.dwSize = sizeof( dscaps );
	lpDS->GetCaps( &dscaps );
	char buf[255];
#ifdef	_DEBUG
	wsprintf( buf, "DS Caps: %d\n",dscaps.dwFlags );
	//OutputDebugString( buf );
#endif

    // Set up the primary direct sound buffer.
    memset(&dsbd, 0, sizeof(DSBUFFERDESC));
    dsbd.dwSize                 = sizeof(DSBUFFERDESC);
    dsbd.dwFlags                = DSBCAPS_PRIMARYBUFFER;
    
    if ((hr = lpDS->CreateSoundBuffer( &dsbd,
                          &lpDSBPrimary,
                          NULL )) != 0)
    {
		return SoundFail( DS_hwndGame, 2 );
    }

	// set primary buffer format if necessary
	if( nType != DS_WAVE_FORMAT_AUTO )
	{
		int nChannels;
		int nSamplesPerSec;
		int nwBitsPerSample;
		switch( nType )
		{
			case	DS_WAVE_FORMAT_2_22_8:
				nChannels =  2;
				nSamplesPerSec = 22050;
				nwBitsPerSample = 8;
				break;
			case	DS_WAVE_FORMAT_2_22_16:
				nChannels =  2;
				nSamplesPerSec = 11025;
				nwBitsPerSample = 8;
				break;
		}
		wfx.wFormatTag = WAVE_FORMAT_PCM;
		wfx.nChannels = 2;
		wfx.nSamplesPerSec = 11025;
		wfx.wBitsPerSample = 8;
		wfx.nBlockAlign = wfx.wBitsPerSample*wfx.nChannels/8;
		wfx.nAvgBytesPerSec = wfx.nSamplesPerSec*wfx.nBlockAlign;
		wfx.cbSize = 0;
		if ((hr = lpDSBPrimary->SetFormat(&wfx)) != DS_OK)
		{
			switch( hr )
			{
			case	DSERR_BADFORMAT:
				strcpy( buf," Bad format " );
				break;
			case	DSERR_INVALIDCALL:
				strcpy( buf," Invalid call " );
				break;
			case	DSERR_INVALIDPARAM:
				strcpy( buf," Invalid Parem " );
				break;
			case	DSERR_OUTOFMEMORY:
				strcpy( buf," Out of memory " );
				break;
			case	DSERR_PRIOLEVELNEEDED:
				strcpy( buf," Priority level needed " );
				break;
			}
			return SoundFail( DS_hwndGame, 3 );
		}
	}

	// always play in primary buffer
	lpDSBPrimary->Play( 0, 0, DSBPLAY_LOOPING );

	// for multi threads
	if( bCreateMutex == FALSE )
	{
		hMutex = CreateMutex( NULL, FALSE, NULL );
		InitializeCriticalSection (&csPlayKey);
		bCreateMutex = TRUE;
	}

	// initialize secondary sound buffers
	for( int i=0; i< WAVE_OBJECT_MAX; i++ )
	{
		lpwdWaves[i] = NULL;
	}
	for( int i=0; i< WAVE_OBJECT_MAXS; i++ )
	{
		lpwsWaves[i] = NULL;
	}

	// enable sound
	DS_bInitialized = TRUE;
	DS_EnableSound( TRUE );

	return TRUE;

}

// release sound device
void DS_QuitSound( void )
{
	if( DS_bInitialized == FALSE )
	{
		OutputDebugString( "DS_QuitSound Warning: No sound at all!\n" );
		return;
	}
	else
	{
		// disable sound
		DS_EnableSound( FALSE );
		DS_bInitialized = FALSE;
		// for multi threads
		if( bCreateMutex == TRUE )
		{
			CloseHandle( hMutex );
			DeleteCriticalSection (&csPlayKey);
			bCreateMutex = FALSE;
		}
	}

	if( lpDS )
	{
		if( lpDSBPrimary != NULL )
		{
			lpDSBPrimary->Stop();
			lpDSBPrimary->Release();
			lpDSBPrimary = NULL;
		}

		lpDS->Release( );
		lpDS = NULL;
	}

}

// init fail message
BOOL SoundFail( HWND hWnd, int err )
{
	char buf[129];

    wsprintf(buf, "DirectSound init FAILED (%d):\n", err+DS_ERROR_ID);
	switch( err )
	{
	case	0:
		strcat( buf, "Direct Sound Creation Failed" );
		break;
	case	1:
		strcat( buf, "Direct Sound Set Cooperative Level Failed" );
		break;
	case	2:
		strcat( buf, "Cannot create primary buffer" ); 
		break;
	}

	DS_QuitSound();

	OutputDebugString( buf );
	return FALSE;
//	if( MessageBox(hWnd, buf, "Direct Sound Error", MB_OK|MB_ICONSTOP) == IDOK )
//			return TRUE;
//	else	return FALSE;
}

// enable or disable sound effects
// bEnable		:	TRUE for enable sound
// return value	:	old state of sound, enable or disable
BOOL DS_EnableSound( BOOL bEnable )
{
	BOOL bEnableSave = DS_bSoundEnable;
	if( DS_bInitialized == FALSE )
		return FALSE;
	if( bEnable )
	{
		DS_bSoundEnable = TRUE;
	}
	else
	{
		DS_bSoundEnable = FALSE;
		// check and destroy sound buffers
		for( int i=0; i< WAVE_OBJECT_MAX; i++ )
		{
			//OutputDebugString( "Wait0," );
			EnterCriticalSection (&csPlayKey);
			WaitForSingleObject( hMutex, INFINITE );
			if( lpwdWaves[i] != NULL )
			{
				int nID = lpwdWaves[i]->nID;
			ReleaseMutex( hMutex );
			//OutputDebugString( "End Wait0\n" );
				lpwdWaves[i]->Stop();
			//OutputDebugString( "Wait1," );
			WaitForSingleObject( hMutex, INFINITE );
				PostMessage( DS_hwndGame, WM_DSSTREAM_DONE, (WPARAM)nID, (LPARAM)0 );
				//OutputDebugString( "Warning: Destroy dynamic sound object before stop playing sound!\n" );
			}
			ReleaseMutex( hMutex );
			LeaveCriticalSection (&csPlayKey);
			//OutputDebugString( "End Wait1\n" );
		}

		//-----debug
#ifdef	_DS_LOG_
		FILE*fp=fopen( "DSTest.log","at" );
		fprintf(fp,"G" );
		fclose(fp);
#endif
		//-----debug

		for( int i=0; i< WAVE_OBJECT_MAXS; i++ )
		{
			EnterCriticalSection (&csPlayKey);
//			WaitForSingleObject( hMutex, INFINITE );
			if( lpwsWaves[i] != NULL )
			{
				int nID = lpwsWaves[i]->nID;
//				ReleaseMutex( hMutex );
				lpwsWaves[i]->Stop();
//				WaitForSingleObject( hMutex, INFINITE );
				PostMessage( DS_hwndGame, WM_DSSTREAM_DONE, (WPARAM)nID, (LPARAM)0 );
			}
//			ReleaseMutex( hMutex );
			LeaveCriticalSection (&csPlayKey);
		}
	}
	return bEnableSave;
}

// test if sound is enable
BOOL DS_IfSoundEnable()
{
	return DS_bSoundEnable;
}

// nFade		:	fade mode, see above
// return value	:	old fade mode
int	DS_FadeSound( int nFadeMode )
{
	int nFadeSave = DS_nFadeMode;
	DS_nFadeMode = nFadeMode;
	return nFadeSave;
}

// set volume
// real range is from 0 to -10000,
// this function is range from 0 to -5000
// under -3200 I cannot hear at all
// nVol			:	volume of the sound device
// return value	:	old volume of the sound device, -1 if error occurs
int	DS_VolumeSound( int nVol )
{
	int nVolume;
	nVolume = DS_nVolume;
	if( nVol >0 ) nVol = 0;
	if( nVol < -5000 ) nVol = -5000;
	DS_nVolume = nVol;
#ifdef	_DEBUG
	OutputDebugString( "DS Volume:" );
	OutputString( DS_nVolume, "\n" );
#endif
	return nVolume;
}

// set pan
// real range is from -10000 to 10000, 
// but this function is range from -2000 to 2000
// larger than 1000, right cannot hear
// smaller than -1000 left cannot hear
// nPan			:	pan value for the sound device
// return value	:	old pan value for the sound device
int	DS_PanSound( int nPan )
{
	int nPanSave = DS_nPan;
	if( nPan < - 2000)	nPan = -2000;
	if( nPan > 2000 )	nPan = 2000;
	DS_nPan = nPan;
#ifdef	_DEBUG
	OutputDebugString( "DS Pan:" );
	OutputString( DS_nPan, "\n" );
#endif
	return nPan;
}

// get sound volume
inline int DS_GetVolume()
{
	return DS_nVolume;	
}
///////////////////


//==========================================================================
//
// class WAVEDYNAMIC
//
//==========================================================================
///////////////////
// global wave play
// used by class WAVEDYNAMIC

// get WAVEDYNAMIC pointer
// nIndex		:	ID in global wave array
// return value	:	WAVEDYNAMIC pointer
LPWAVEDYNAMIC SetToGlobal( int nIndex );

// nIndex		:	ID in global wave array
// return value	:	0 if succeeded, otherwise error number,
int StreamBufferSetup( int nIndex );

// nIndex	:	ID in global wave array
void StreamBufferRelease( int nIndex );

// nIndex	:	ID in global wave array
void ResetWavePlayer( int nIndex );

// nIndex	:	ID in global wave array
void CALLBACK TimeFunc( UINT, UINT, DWORD nIndex, DWORD, DWORD );

// constructor
CDynamicWave::CDynamicWave()
{
    pwfx = NULL;             /* Wave Format data structure */
    hmmio = 0;             /* MM I/O handle for the WAVE */
    mmck;              /* Multimedia RIFF chunk */
    memset( &mmckInRIFF,0, sizeof( mmckInRIFF ) );        /* Use in opening a WAVE file */
    lpDSBStreamBuffer = NULL; /* Points to DirectSoundBuffer */
    dwBufferSize = 0;      /* Size of the entire buffer */
    dwBufferSegSize = 0;   /* Size of one buffer segment */
    dwNextWriteOffset = 0; /* Offset to next buffer segment */
    dwPlayLast = 0;        /* Stores last play cursor */
    dwProgress = 0;        /* Used with above to show prog. */
    bDonePlaying = TRUE;      /* Signals early abort to timer */
    bLoopFile = FALSE;         /* Should we loop playback? */
    bFoundEnd = TRUE;         /* Timer found file end */
    nRemainingSegments = 0;/* Segments 'til timer shutdown */
	
	//char	strFileName[65];				/* wave file name */
	nID = WAVE_OBJECT_NONE;				// ID, and if wave file has been opened 
	bPlaying =  FALSE, bTimerInstalled = FALSE;	// if is playing, timer has been installed
	uTimerID = 0, uLastPercent = 0;		// timer ID and percent has been played 
//	return 1;
}

// destructor
CDynamicWave::~CDynamicWave()
{
	if( nID != WAVE_OBJECT_NONE )
	{
        PostMessage( DS_hwndGame, WM_DSSTREAM_DONE, (WPARAM)nID, (LPARAM)0 );
		Stop();
		OutputDebugString( "DS Warning: not stop playing sound before destroy sound buffer!\n" );
	}
}

// load sound buffer
// filename		:	wave file name
// return value	:	TRUE if succeeded
BOOL WAVEDYNAMIC::Load( LPCTSTR filename )
{
	// register this sound buffer to global array
	BOOL bSuccess = FALSE;
	for( int i=0; i< WAVE_OBJECT_MAX; i++ )
	{
		if( lpwdWaves[i] == NULL )
		{
			lpwdWaves[i] = this;
			nID = i;
			bSuccess = TRUE;
#ifdef	__TEST_SOUND__
			for( int x=0; x<WAVE_OBJECT_MAX; x++ )
			{
				if( nID == x )	continue;
				if( !lpwdWaves[x] ) continue;
				ASSERT( this != lpwdWaves[x] );
			}
#endif
			break;
		}
	}
	if( !bSuccess )
	{
		return FALSE;
	}
	
	// load sound with a wave format file
	strcpy( strFileName, filename );
	if(StreamBufferSetup( nID ) != 0 )
	{
		// Error opening the WAVE file so abort
		lpwdWaves[nID] = NULL;
		nID = WAVE_OBJECT_NONE;
		return FALSE;
	}
	nWaveCounter++;

#ifdef	__TEST_SOUND__
	ASSERT( lpwdWaves[nID]->lpDSBStreamBuffer );
#endif

#ifdef	_DEBUG
	char	tmp[33];
	itoa( nWaveCounter, tmp, 10 );
//	OutputDebugString( "DS Dynamic wave Load:" );
//	OutputDebugString(tmp);
//	OutputDebugString("\n");
#endif

	return TRUE;
}

// release sound buffer
void WAVEDYNAMIC::Close()
{
	if( nID == WAVE_OBJECT_NONE )	return;
	//OutputDebugString( "Wait2," );
	WaitForSingleObject( hMutex, INFINITE );

	// release sound buffer, and unregister from global array
	StreamBufferRelease( nID );
	lpwdWaves[nID] = NULL;
	nID = WAVE_OBJECT_NONE;
	nWaveCounter--;

	ReleaseMutex( hMutex );
	//OutputDebugString( "End Wait2\n" );
#ifdef	_DEBUG
	char	tmp[33];
	itoa( nWaveCounter, tmp, 10 );
//	OutputDebugString( "DS Dynamic wave Close:" );
//	OutputDebugString(tmp);
//	OutputDebugString("\n");
#endif
}

// play wave file
// dwFlags		:	TRUE if looping
// pos			:	position to play from
// return value	:	TRUE if played
// if this object is playing, stop and playing again
// Warning:	a bug report here: the last sound in segment buffer 
//          cannot be played, so you need the file larger than 
//          normal at last, with a period of slience.
BOOL WAVEDYNAMIC::Play( DWORD dwFlags/*=DSBPLAY_LOOPING*/, DWORD pos/*=0*/ )
{
    HRESULT dsrval;
	
	if( !DS_bSoundEnable )	return TRUE;

	if( bPlaying == TRUE )
	{
        PostMessage( DS_hwndGame, WM_DSSTREAM_DONE, (WPARAM)nID, (LPARAM)0 );
		Stop();
	}

	if( !Load( strFileName ) )	
		return FALSE;

#ifdef	__TEST_SOUND__
	ASSERT( nID != WAVE_OBJECT_NONE );
	ASSERT( lpDSBStreamBuffer );
#endif

#ifdef	__TEST_SOUND__
	ASSERT( bPlaying == FALSE );
#endif
    // Ensure that position is at 0, ready to go
	// set position
    if( lpDSBStreamBuffer->SetCurrentPosition( pos ) != DS_OK )
		return FALSE;

	// set volume
    if( lpDSBStreamBuffer->SetVolume( DS_nVolume ) != DS_OK )
		return FALSE;

	// set pan
    if( lpDSBStreamBuffer->SetPan( DS_nPan ) != DS_OK )
		return FALSE;

	// play
    if( lpDSBStreamBuffer->Play( 0, 0, DSBPLAY_LOOPING ) != DS_OK )
		return FALSE;
	bPlaying = TRUE;

    if( timeBeginPeriod( PLAYBACK_TIMER_PERIOD
                            / PLAYBACK_OVERSAMPLE ) != TIMERR_NOERROR )
        {
        /* Can't create timer! */
        dsrval = lpDSBStreamBuffer->Stop();
        bPlaying = bTimerInstalled = FALSE;
		Close();
        return FALSE;
        }
    else
    {
        if(( uTimerID = timeSetEvent( PLAYBACK_TIMER_PERIOD
                                       / PLAYBACK_OVERSAMPLE,
                                       PLAYBACK_TIMER_ACCURACY,
                                       TimeFunc, (DWORD)nID,
                                       /*TIME_ONESHOT*/TIME_PERIODIC )) != 0 )
		{
			bTimerInstalled = TRUE;
#ifdef	__TEST_SOUND__
			for( int i=0; i< WAVE_OBJECT_MAX; i++ )
			{
				if( i == nID )	continue;
				if( !lpwdWaves[i] ) continue;
				ASSERT( uTimerID != lpwdWaves[i]->uTimerID );
			}
#endif
		}
		else
		{
			timeEndPeriod( PLAYBACK_TIMER_PERIOD
		                   / PLAYBACK_OVERSAMPLE );
	        dsrval = lpDSBStreamBuffer->Stop();
			bPlaying = bTimerInstalled = FALSE;
			Close();
			return FALSE;
		}
    }
	bLoopFile = dwFlags;
	return TRUE;
}

// reset wave player, avoid to use
void WAVEDYNAMIC::Reset()
{
	ResetWavePlayer( nID );
}

// stop playing
// if has stoped, no effects
void WAVEDYNAMIC::Stop()
{
    HRESULT dsrval;

	if( nID == WAVE_OBJECT_NONE )	return;
	WaitForSingleObject( hMutex, INFINITE );

#ifdef	__TEST_SOUND__
    ASSERT( !bDonePlaying );
#endif
	bDonePlaying = TRUE;
#ifdef	__TEST_SOUND__
	ASSERT( bTimerInstalled );
#endif
    if( bTimerInstalled )
    {
        bTimerInstalled = FALSE;
        timeKillEvent( uTimerID );
        timeEndPeriod( PLAYBACK_TIMER_PERIOD
                        / PLAYBACK_OVERSAMPLE );
		uTimerID = 0;
    }
#ifdef	__TEST_SOUND__
	ASSERT( bPlaying );
#endif
    if( bPlaying )
    {
        bPlaying = FALSE;
		dsrval = lpDSBStreamBuffer->Stop();
		ReleaseMutex( hMutex );
		Close();
		WaitForSingleObject( hMutex, INFINITE );
    }

	ReleaseMutex( hMutex );
}

// for fade out
// to make the sound lower and lower
// return value	:	TRUE if succeeded
BOOL WAVEDYNAMIC::FadeOut()
{
	LONG lVolume;
	int	 nStep;
	BOOL retrn = FALSE;
	if( lpDSBStreamBuffer->GetVolume( &lVolume ) == DS_OK )
	{
		nStep = 100;//0 - lVolume+50;
		if( lVolume-nStep > -2000 )
			lVolume = lVolume - nStep;
		else
		{
			lVolume = -2000; retrn = TRUE;
		}
		if( lpDSBStreamBuffer->SetVolume( lVolume ) != DS_OK )
		{
			retrn = TRUE;
		}
	}
	else	retrn = TRUE;
	return retrn;
}

// to make the sound louder and louder
// return value	:	TRUE if succeeded
BOOL WAVEDYNAMIC::FadeIn()
{
	LONG lVolume;
	int	 nStep;
	BOOL retrn = FALSE;
	if( lpDSBStreamBuffer->GetVolume( &lVolume ) == DS_OK )
	{
		nStep = 100;//0 - lVolume + 50;
		if( lVolume+nStep > 0 )
		{
			lVolume = 0; retrn = TRUE;
		}
		else
		{
			lVolume = lVolume + nStep;
		}
		if( lpDSBStreamBuffer->SetVolume( lVolume ) != DS_OK )
		{
			retrn = TRUE;
		}
	}
	else	retrn = TRUE;
	return retrn;
}
//////////////////

// functinos used by WAVEDYNAMIC
//////////////////
/*****************************************************************************/
/* SetToGlobal()															 */
/*                                                                           */
/* this functions call is to get WAVEDYNAMIC object from global array		 */
/*                                                                           */
/*****************************************************************************/
// nIndex		:	ID in global wave array
// return value	:	WAVEDYNAMIC pointer
inline LPWAVEDYNAMIC SetToGlobal( int nIndex )
{
	return lpwdWaves[nIndex];
}

/*****************************************************************************/
/* StreamBufferSetup()                                                       */
/*                                                                           */
/*    This function uses the filename stored in the global character array to*/
/* open a WAVE file. Then it creates a secondary DirectSoundBuffer object    */
/* which will later be used to stream that file from disk during playback.   */
/*                                                                           */
/*****************************************************************************/
// nIndex		:	ID in global wave array
// return value	:	0 if succeeded, otherwise error number,
int StreamBufferSetup( int nIndex )
    {
    DSBUFFERDESC dsbd;
    HRESULT      dsRetVal;
    LPBYTE       lpWrite1, lpWrite2;
    DWORD        dwLen1, dwLen2;
    UINT         uChkErr;
        
    int nChkErr;
	LPWAVEDYNAMIC lpwiWave = NULL;

	// set lpwdWaves[nIndex] to lpwiWave
	// so that you can use lpwiWave directly
#ifdef	__TEST_SOUND__
	ASSERT( nIndex >= 0 );
#endif
	lpwiWave = SetToGlobal( nIndex );

#ifdef	__TEST_SOUND__
	ASSERT( lpwdWaves[nIndex] == lpwiWave );
#endif

    /* This portion of the WAVE I/O is patterned after what's in DSTRWAVE, which
     * was in turn adopted from WAVE.C which is part of the DSSHOW sample.
     */

    if(( nChkErr = WaveOpenFile( lpwiWave->strFileName, &lpwiWave->hmmio, &lpwiWave->pwfx, &lpwiWave->mmckInRIFF )) != 0 )
        {
        nChkErr =  ERR_WAVE_OPEN_FAILED;
	    //SoundFail( DS_hwndGame, nChkErr );
		goto END_SETUP;
        }

#ifdef	__TEST_SOUND__
	ASSERT( lpwdWaves[nIndex] == lpwiWave );
#endif

    if( lpwiWave->pwfx->wFormatTag != WAVE_FORMAT_PCM )
        {
		nChkErr = ERR_WAVE_INVALID_FORMAT;
        //SoundFail( DS_hwndGame, nChkErr );
        WaveCloseReadFile( &lpwiWave->hmmio, &lpwiWave->pwfx );
		goto END_SETUP;
        }
    /* Seek to the data chunk */
    if(( nChkErr = WaveStartDataRead( &lpwiWave->hmmio, &lpwiWave->mmck, &lpwiWave->mmckInRIFF )) != 0 )
        {
		nChkErr = ERR_WAVE_CORRUPTED_FILE;
        //SoundFail( DS_hwndGame, nChkErr );
        WaveCloseReadFile( &lpwiWave->hmmio, &lpwiWave->pwfx );
		goto END_SETUP;
        }
    /* As a side note, mmck.ckSize will be the size of all the data in this file.
     * That's something which might be handy when calculating the length... */

#ifdef	__TEST_SOUND__
	ASSERT( lpwdWaves[nIndex] == lpwiWave );
#endif

    /* Calculate a buffer length, making sure it is an exact multiple of the
     * buffer segment size.
     */
    lpwiWave->dwBufferSize = ((DWORD)lpwiWave->pwfx->nAvgBytesPerSec
                            * (((NUM_BUFFER_SEGMENTS * PLAYBACK_TIMER_PERIOD)
                            / 10)) / 100);

#ifdef	__TEST_SOUND__
	ASSERT( lpwdWaves[nIndex] == lpwiWave );
#endif

    lpwiWave->dwBufferSegSize = lpwiWave->dwBufferSize / NUM_BUFFER_SEGMENTS;
    lpwiWave->dwBufferSize = lpwiWave->dwBufferSegSize * NUM_BUFFER_SEGMENTS;

    /*
     * Create the secondary DirectSoundBuffer object to receive our sound data.
     */
    memset( &dsbd, 0, sizeof( DSBUFFERDESC ));
    dsbd.dwSize = sizeof( DSBUFFERDESC );
    dsbd.dwFlags = DSBCAPS_CTRLDEFAULT;
    dsbd.dwBufferBytes = lpwiWave->dwBufferSize;

#ifdef	__TEST_SOUND__
	ASSERT( lpwdWaves[nIndex] == lpwiWave );
#endif

    /* Set Format properties according to the WAVE file we just opened */
	//LPDIRECTSOUNDBUFFER     lpDSB = NULL;
    dsbd.lpwfxFormat = lpwiWave->pwfx;
    dsRetVal = lpDS->CreateSoundBuffer( &dsbd,
                                        &lpwiWave->lpDSBStreamBuffer,
                                        NULL );
    if( dsRetVal != DS_OK )
        {
		nChkErr = ERR_CREATEDSB_FAILED;
        //SoundFail( DS_hwndGame, nChkErr );
        WaveCloseReadFile( &lpwiWave->hmmio, &lpwiWave->pwfx );
		goto END_SETUP;
        }
/*
	// set wave format 
    if ((dsRetVal = lpDSBPrimary->SetFormat(&wfx)) != DS_OK)
	{
		nChkErr = ERR_WAVE_FORMAT_INVALID;
		SoundFail( DS_hwndGame, nChkErr );
        WaveCloseReadFile( &lpwiWave->hmmio, &lpwiWave->pwfx );
		goto END_SETUP;
	}
*/
#ifdef	__TEST_SOUND__
	ASSERT( lpwiWave->lpDSBStreamBuffer );
#endif
#ifdef	__TEST_SOUND__
	ASSERT( lpwdWaves[nIndex] == lpwiWave );
#endif

    //lpwiWave->lpDSBStreamBuffer = lpDSBStreamBuffer;
    lpwiWave->bFoundEnd = FALSE;
    lpwiWave->nRemainingSegments = 0;

    dsRetVal = lpwiWave->lpDSBStreamBuffer->Lock( 0, lpwiWave->dwBufferSize,
								               (void**)&lpWrite1, &dwLen1,
											   (void**)&lpWrite2, &dwLen2,
											   0 );
    if( dsRetVal != DS_OK )
        {
		nChkErr = ERR_CREATEDSB_LOST;
        //SoundFail( DS_hwndGame, nChkErr );
        WaveCloseReadFile( &lpwiWave->hmmio, &lpwiWave->pwfx );
		lpwiWave->lpDSBStreamBuffer->Release();
		lpwiWave->lpDSBStreamBuffer = NULL;
		goto END_SETUP;
        }

#ifdef	__TEST_SOUND__
	ASSERT( lpwiWave->lpDSBStreamBuffer );
#endif
#ifdef	__TEST_SOUND__
	ASSERT( lpwdWaves[nIndex] == lpwiWave );
#endif

    if( dwLen1 )
        {
        nChkErr = WaveReadFile( lpwiWave->hmmio, (UINT)dwLen1, lpWrite1,
                                &lpwiWave->mmck, &uChkErr );
        if( uChkErr < dwLen1 )
            {
            if( lpwiWave->bLoopFile )
                {
    /* If the file is shorter than the buffer and we're looping, we need to
     * read the file in again so that we don't get a block of silence before
     * the timer loops playback.
     */
                LPBYTE lpTemp = lpWrite1;

                do
                    {
                    /* Continue decrementing our count and moving our temp
                     * pointer forward until we've read the file enough times
                     * to fill the buffer.  NOTE: It's probably not efficient
                     * to bother with the overhead of streaming a file that's
                     * not at least as large as the buffer... */
                    lpTemp += uChkErr;
                    dwLen1 -= uChkErr;
                    nChkErr = WaveStartDataRead( &lpwiWave->hmmio,
                                                    &lpwiWave->mmck,
                                                    &lpwiWave->mmckInRIFF );
                    nChkErr = WaveReadFile( lpwiWave->hmmio, (UINT)dwLen1,
                                            lpTemp,
                                            &lpwiWave->mmck, &uChkErr );
                    } while( uChkErr < dwLen1 );
                }
            else
                {
                lpwiWave->bFoundEnd = TRUE;
                lpwiWave->nRemainingSegments = (int)(uChkErr /
                                                    lpwiWave->dwBufferSegSize);
//                DPF( 3,"Setting bFoundEnd in load, nRemSegs = %i, dwBuffSegSize = %u",
  //                      lpwiWave->nRemainingSegments, lpwiWave->dwBufferSegSize );
                memset( (lpWrite1+uChkErr),
                                lpwiWave->pwfx->wBitsPerSample == 8 ? 128 : 0,
                                (dwLen1 - uChkErr));
                }
            }
        }

#ifdef	__TEST_SOUND__
	ASSERT( lpwdWaves[nIndex] == lpwiWave );
#endif

    dsRetVal = lpwiWave->lpDSBStreamBuffer->Unlock( (LPVOID)lpWrite1, dwLen1,
                                        (LPVOID)lpWrite2, 0 );

#ifdef	__TEST_SOUND__
	ASSERT( lpwiWave->lpDSBStreamBuffer );
#endif
#ifdef	__TEST_SOUND__
	ASSERT( lpwdWaves[nIndex] == lpwiWave );
#endif

    lpwiWave->dwNextWriteOffset = lpwiWave->dwPlayLast = lpwiWave->dwProgress = 0;
    lpwiWave->bDonePlaying = FALSE;
    lpwiWave->bLoopFile = FALSE;

#ifdef	__TEST_SOUND__
	ASSERT( lpwiWave->lpDSBStreamBuffer );
#endif
#ifdef	__TEST_SOUND__
	ASSERT( lpwdWaves[nIndex] == lpwiWave );
#endif

	// LPARAM	:	 if it is dynamic wave object
    SendMessage( DS_hwndGame, WM_DSSTREAM_PROGRESS, 0L, 0L );
	nChkErr = 0;
END_SETUP:
    return( nChkErr );
}

/*****************************************************************************/
/* StreamBufferRelease()                                                     */
/*																			 */
/*  close Stream wave file, and kill timer if nessacary.                     */
/*                                                                           */
/*****************************************************************************/
// nIndex	:	ID in global wave array
void StreamBufferRelease( int nIndex )
{
	LPWAVEDYNAMIC lpwiWave = NULL;

	// set lpwdWaves[nIndex] to lpwiWave
	// so that you can use lpwiWave directly
	lpwiWave = SetToGlobal( nIndex );

	WaveCloseReadFile( &lpwiWave->hmmio, &lpwiWave->pwfx );
#ifdef	__TEST_SOUND__
	ASSERT( lpwiWave->lpDSBStreamBuffer );
#endif
	if( lpwiWave->lpDSBStreamBuffer )
		lpwiWave->lpDSBStreamBuffer->Release();
	lpwiWave->lpDSBStreamBuffer = NULL;

}

/*****************************************************************************/
/* ResetWavePlayer()                                                         */
/*                                                                           */
/*  Performs a subset of the above operations (in StreamBufferSetup). Things */
/* not done include creating a DSB and opening the file (it's already open). */
/*                                                                           */
/*****************************************************************************/
// nIndex	:	ID in global wave array
void ResetWavePlayer( int nIndex )
{
    LPBYTE      lpWrite1, lpWrite2;
    DWORD       dwLen1, dwLen2;
    UINT        uChkErr;
    int         nChkErr;
    HRESULT     dsrval;
	
	LPWAVEDYNAMIC lpwiWave = NULL;

	// set lpwdWaves[nIndex] to lpwiWave
	// so that you can use lpwiWave directly
	lpwiWave = SetToGlobal( nIndex );

    WaveStartDataRead( &lpwiWave->hmmio, &lpwiWave->mmck, &lpwiWave->mmckInRIFF );
    lpwiWave->bFoundEnd = FALSE;
    lpwiWave->nRemainingSegments = 0;

    dsrval = lpwiWave->lpDSBStreamBuffer->Lock( 0, lpwiWave->dwBufferSize,
                                      (void**)&lpWrite1, &dwLen1,
                                      (void**)&lpWrite2, &dwLen2,
                                      0 );

	if( dsrval != DS_OK )
	{
		char	errStr[129];
		switch ( dsrval )
		{
		case	DSERR_BUFFERLOST: 
			strcpy(errStr, "DSERR_BUFFERLOST" );
			break;
		case	DSERR_INVALIDPARAM:
			strcpy(errStr, "DSERR_INVALIDPARAM" );
			break;
		case	DSERR_INVALIDCALL:
			strcpy(errStr, "DSERR_INVALIDCALL" );
			break;
		case	DSERR_PRIOLEVELNEEDED:
			strcpy(errStr, "DSERR_PRIOLEVELNEEDED" );
			break;
		}
		strcat( errStr, "\n" );
		//OutputDebugString( "TimeFunc() could not lock DirectSoundBuffer" );
		OutputDebugString( errStr );
        return;
	}
    if( dwLen1 )
        {
        nChkErr = WaveReadFile( lpwiWave->hmmio, (UINT)dwLen1, lpWrite1,
                                &lpwiWave->mmck, &uChkErr );
        if( uChkErr < dwLen1 )
            {
            if( lpwiWave->bLoopFile )
                {
    /* If the file is shorter than the buffer and we're looping, we need to
     * read the file in again so that we don't get a block of silence before
     * the timer loops playback.
     */
                LPBYTE lpTemp = lpWrite1;

                do
                    {
                    /* Continue decrementing our count and moving our temp
                     * pointer forward until we've read the file enough times
                     * to fill the buffer.  NOTE: It's probably not efficient
                     * to bother with the overhead of streaming a file that's
                     * not at least as large as the buffer... */
                    lpTemp += uChkErr;
                    dwLen1 -= uChkErr;
                    nChkErr = WaveStartDataRead( &lpwiWave->hmmio,
                                                    &lpwiWave->mmck,
                                                    &lpwiWave->mmckInRIFF );
                    nChkErr = WaveReadFile( lpwiWave->hmmio, (UINT)dwLen1,
                                            lpTemp,
                                            &lpwiWave->mmck, &uChkErr );
                    } while( uChkErr < dwLen1 );
                }
            else
                {
                lpwiWave->bFoundEnd = TRUE;
                lpwiWave->nRemainingSegments = (int)(uChkErr / lpwiWave->dwBufferSegSize);
//                DPF( 3,"Setting bFoundEnd in load, nRemSegs = %i, dwBuffSegSize = %u",
  //                      lpwiWave->nRemainingSegments, lpwiWave->dwBufferSegSize );

                // Cover ourselves by filling the rest of the buffer space
                // with 8 or 16 bit silence, in case we can't stop the playback
                // exactly on a block boundary and we run a bit into NULL data
                memset(( lpWrite1 + uChkErr),
                                lpwiWave->pwfx->wBitsPerSample == 8 ? 128 : 0,
                                dwLen1 - uChkErr);
                }
            }
        }
    dsrval = lpwiWave->lpDSBStreamBuffer->Unlock( (LPVOID)lpWrite1, dwLen1,
                                        (LPVOID)lpWrite2, 0 );
    lpwiWave->dwNextWriteOffset = lpwiWave->dwPlayLast = lpwiWave->dwProgress = 0;
    lpwiWave->bDonePlaying = FALSE;
	// LPARAM	:	0 if it is dynamic wave object
    SendMessage( DS_hwndGame, WM_DSSTREAM_PROGRESS, 0L, 0L );

}

// nIndex	:	ID in global wave array
void CALLBACK TimeFunc( UINT uTimerID, UINT uMsg, DWORD dwUser,
                                                        DWORD dw1, DWORD dw2 )
{
    static BOOL bInTimer = FALSE;
    LPBYTE      lpWrite1, lpWrite2, lpTemp;
    DWORD       dwLen1, dwLen2, dwPlay, dwWrite;
    int         nChkErr;
    UINT        uChkErr;

	HRESULT		dsrval;
	LPWAVEDYNAMIC lpwiWave = NULL;

    if( bInTimer )
        {
        return;
        }
    else
        {
        bInTimer = TRUE;
        }

	WaitForSingleObject( hMutex, INFINITE );

	// set lpwdWaves[nIndex] to lpwiWave
	// so that you can use lpwiWave directly
	lpwiWave = SetToGlobal( dwUser );

	// very rarely to meet this situation
	// if the thread is stoping the wave, and this function begin to wait,
	// when stoped, the dwUser is invalid, and lpwiWave is invalid too.
	// so must test if should quit.
	if( !lpwiWave )
	{
		OutputDebugString( "DS dynamic error: just begin to play when stop wave!\n" );
		//-----debug
#ifdef	_DS_LOG_
		FILE*fp=fopen( "DSTest.log","at" );
		fprintf(fp,"A" );
		fclose(fp);
#endif
		//-----debug
		goto END_TIME;
	}

#ifdef	__TEST_SOUND__
	ASSERT( lpwiWave->uTimerID == uTimerID );
#endif
#ifdef	__TEST_SOUND__
	ASSERT( lpwdWaves[dwUser] == lpwiWave );
#endif

    /* Get and print the current position of the play cursor */
    lpwiWave->lpDSBStreamBuffer->GetCurrentPosition( &dwPlay, &dwWrite );

    if(( lpwiWave->dwNextWriteOffset < dwPlay )
        && (( lpwiWave->dwNextWriteOffset + lpwiWave->dwBufferSegSize ) > dwPlay ))
    {
/*		OutputDebugString( "DS dynamic error: Write offset!\n" );
		//-----debug
		FILE*fp=fopen( "DSTest.log","at" );
		fprintf(fp,"B" );
		fclose(fp);
		//-----debug
*/
        goto END_TIME;
    }

#ifdef	__TEST_SOUND__
	ASSERT( lpwdWaves[dwUser] == lpwiWave );
#endif

    /* Have we found the end of the file and passed the buffer end? */
    if( lpwiWave->bFoundEnd && !lpwiWave->nRemainingSegments )
    {
        if( !lpwiWave->bDonePlaying )
        {
            PostMessage( DS_hwndGame, WM_DSSTREAM_DONE, (WPARAM)lpwiWave->nID, (LPARAM)0 );
            //lpwiWave->bDonePlaying = TRUE;
			lpwiWave->Stop();
        }
        goto END_TIME;
    }

#ifdef	__TEST_SOUND__
	ASSERT( lpwdWaves[dwUser] == lpwiWave );
#endif

    if( dwPlay < lpwiWave->dwPlayLast )
        lpwiWave->dwProgress += (dwPlay + lpwiWave->dwBufferSize - lpwiWave->dwPlayLast);
    else
        lpwiWave->dwProgress += (dwPlay - lpwiWave->dwPlayLast);
    lpwiWave->dwPlayLast = dwPlay;
    PostMessage( DS_hwndGame, WM_DSSTREAM_PROGRESS, 0L, 0L );

#ifdef	__TEST_SOUND__
	ASSERT( lpwdWaves[dwUser] == lpwiWave );
#endif

    /*
     *  If lpwiWave->bFoundEnd == TRUE, then we've finished reading in the file,
     * but we need to wait until the buffer's play cursor passes the point we
     * were at when we found out we were done reading.
     */
    if( lpwiWave->bFoundEnd && lpwiWave->nRemainingSegments )
        {
        lpwiWave->nRemainingSegments--;

        if( lpwiWave->nRemainingSegments > 1 ) {
        dsrval = lpwiWave->lpDSBStreamBuffer->Lock( lpwiWave->dwNextWriteOffset,
                                            lpwiWave->dwBufferSegSize,
                                            (void**)&lpWrite1, &dwLen1,
                                            (void**)&lpWrite2, &dwLen2,
                                            0 );

		if( dsrval != DS_OK )
        {
			char	errStr[129];
			switch ( dsrval )
			{
			case	DSERR_BUFFERLOST: 
				strcpy(errStr, "DSERR_BUFFERLOST" );
				break;
			case	DSERR_INVALIDPARAM:
				strcpy(errStr, "DSERR_INVALIDPARAM" );
				break;
			case	DSERR_INVALIDCALL:
				strcpy(errStr, "DSERR_INVALIDCALL" );
				break;
			case	DSERR_PRIOLEVELNEEDED:
				strcpy(errStr, "DSERR_PRIOLEVELNEEDED" );
				break;
			}
			strcat( errStr, "\n" );
			//OutputDebugString( "TimeFunc() could not lock DirectSoundBuffer" );
			//-----debug
#ifdef	_DS_LOG_
			FILE*fp=fopen( "DSTest.log","at" );
			fprintf(fp,"C" );
			fclose(fp);
#endif
			//-----debug
			OutputDebugString( errStr );
            goto END_TIME;
        }

        if( lpwiWave->pwfx->wBitsPerSample == 8 )
            memset( lpWrite1, 128, dwLen1 );
        else if( lpwiWave->pwfx->wBitsPerSample == 16 )
            memset( lpWrite1, 0, dwLen1 );
        lpwiWave->lpDSBStreamBuffer->Unlock( (LPVOID)lpWrite1, dwLen1,
                                          (LPVOID)lpWrite2, dwLen2 );
    /*
     * This code is stolen from the end of the routine -- we need to keep
     * zeroing out buffer segments while we're waiting for the play cursor to
     * catch up to the end of the WAVE data.
     */
        lpwiWave->dwNextWriteOffset += lpwiWave->dwBufferSegSize;
        if( lpwiWave->dwNextWriteOffset >= lpwiWave->dwBufferSize )
            lpwiWave->dwNextWriteOffset -= lpwiWave->dwBufferSize;
        }
        goto END_TIME;
        }

#ifdef	__TEST_SOUND__
	ASSERT( lpwdWaves[dwUser] == lpwiWave );
#endif

    /* Lock a segment of memory that is behind the play cursor */
    if( ( dsrval = lpwiWave->lpDSBStreamBuffer->Lock( lpwiWave->dwNextWriteOffset,
                                        lpwiWave->dwBufferSegSize,
                                        (void**)&lpWrite1, &dwLen1,
                                        (void**)&lpWrite2, &dwLen2,
                                        0 ) ) != DS_OK )
        {
		char	errStr[129];
		switch ( dsrval )
		{
		case	DSERR_BUFFERLOST: 
			strcpy(errStr, "DSERR_BUFFERLOST" );
			break;
		case	DSERR_INVALIDPARAM:
			strcpy(errStr, "DSERR_INVALIDPARAM" );
			break;
		case	DSERR_INVALIDCALL:
			strcpy(errStr, "DSERR_INVALIDCALL" );
			break;
		case	DSERR_PRIOLEVELNEEDED:
			strcpy(errStr, "DSERR_PRIOLEVELNEEDED" );
			break;
		}
		strcat( errStr, "\n" );
        //OutputDebugString( "TimeFunc() could not lock DirectSoundBuffer" );
		//-----debug
#ifdef	_DS_LOG_
		FILE*fp=fopen( "DSTest.log","at" );
		fprintf(fp,"D" );
		fclose(fp);
#endif
		//-----debug
		OutputDebugString( errStr );
        goto END_TIME;
        }
        
#ifdef	__TEST_SOUND__
	ASSERT( lpwdWaves[dwUser] == lpwiWave );
#endif

    if( dwLen1 && !lpwiWave->bDonePlaying )
        {
        nChkErr = WaveReadFile( lpwiWave->hmmio, (UINT)dwLen1, lpWrite1,
                                &lpwiWave->mmck, &uChkErr );
        if( uChkErr < (UINT)dwLen1 )
            {
            if( !lpwiWave->bLoopFile )
                {
                /* Zero out the rest of this block */
                if( lpwiWave->pwfx->wBitsPerSample == 8 )
                    memset( lpWrite1+uChkErr, 128, ((UINT)dwLen1-uChkErr));
                else if( lpwiWave->pwfx->wBitsPerSample == 16 )
                    memset( lpWrite1+uChkErr, 0, ((UINT)dwLen1-uChkErr));
/* Enable play completion detection code at the beginning of the next call */
                lpwiWave->bFoundEnd = TRUE;
                if( dwPlay > lpwiWave->dwNextWriteOffset )
                    lpwiWave->nRemainingSegments = (lpwiWave->dwNextWriteOffset
                                                + lpwiWave->dwBufferSize - dwPlay)
                                                / lpwiWave->dwBufferSegSize;
                else
                    lpwiWave->nRemainingSegments = (lpwiWave->dwNextWriteOffset
                                                - dwPlay)
                                                / lpwiWave->dwBufferSegSize;
                }
             else
                {
                lpTemp = lpWrite1;
                do
                    {
                    /* Continue decrementing our count and moving our temp
                     * pointer forward until we've read the file enough times
                     * to fill the buffer.  NOTE: It's probably not efficient
                     * to bother with the overhead of streaming a file that's
                     * not at least as large as the buffer... */
                    lpTemp += uChkErr;
                    dwLen1 -= uChkErr;
                    nChkErr = WaveStartDataRead( &lpwiWave->hmmio,
                                                    &lpwiWave->mmck,
                                                    &lpwiWave->mmckInRIFF );
					// added by Liu Gang, Aug 13, 1997
					// to fix a bug
					if( nChkErr != 0 )	break;
                    nChkErr = WaveReadFile( lpwiWave->hmmio, (UINT)dwLen1,
                                            lpTemp,
                                            &lpwiWave->mmck, &uChkErr );
					// added by Liu Gang, Aug 13, 1997
					if( nChkErr != 0 )	break;
                    } while( uChkErr < dwLen1 );
                }
            }
        }
    /*
    * The bDonePlaying flag is set by the caller if the user stops playback
    * before the end of the WAVE file is encountered.  It tells us to cut this
    * racket out and play nothing in case it takes the caller a couple
    * interrupts to shut off the timer.
    */
    else if( dwLen1 && lpwiWave->bDonePlaying )
        {
        // Set the appropriate silence value
        memset( lpWrite1,
                    lpwiWave->pwfx->wBitsPerSample == 8 ? 128 : 0,
                    dwLen1);
        }

    if( dwLen2 && !lpwiWave->bDonePlaying )
        {
        nChkErr = WaveReadFile( lpwiWave->hmmio, (UINT)dwLen2, lpWrite2,
                                &lpwiWave->mmck, &uChkErr );
        if( uChkErr < (UINT)dwLen2 )
            {
            if( !lpwiWave->bLoopFile )
                {
                /* Zero out the rest of this block */
                if( lpwiWave->pwfx->wBitsPerSample == 8 )
                    memset( lpWrite2+uChkErr, 128, ((UINT)dwLen2-uChkErr));
                else if( lpwiWave->pwfx->wBitsPerSample == 16 )
                    memset( lpWrite2+uChkErr, 0, ((UINT)dwLen2-uChkErr));
                /* Enable play completion detection code at the beginning
                 * of the next call
                 */
                lpwiWave->bFoundEnd = TRUE;
                if( dwPlay > lpwiWave->dwNextWriteOffset )
                    lpwiWave->nRemainingSegments = (lpwiWave->dwNextWriteOffset
                                                + lpwiWave->dwBufferSize - dwPlay)
                                                / lpwiWave->dwBufferSegSize;
                else
                    lpwiWave->nRemainingSegments = (lpwiWave->dwNextWriteOffset
                                                - dwPlay)
                                                / lpwiWave->dwBufferSegSize;
                }
            else
                {
                lpTemp = lpWrite2;
                do
                    {
                    /* Continue decrementing our count and moving our temp
                     * pointer forward until we've read the file enough times
                     * to fill the buffer.  NOTE: It's probably not efficient
                     * to bother with the overhead of streaming a file that's
                     * not at least as large as the buffer... */
                    lpTemp += uChkErr;
                    dwLen2 -= uChkErr;
                    nChkErr = WaveStartDataRead( &lpwiWave->hmmio,
                                                    &lpwiWave->mmck,
                                                    &lpwiWave->mmckInRIFF );
                    nChkErr = WaveReadFile( lpwiWave->hmmio, (UINT)dwLen2,
                                            lpTemp,
                                            &lpwiWave->mmck, &uChkErr );
                    } while( uChkErr < dwLen2 );
                }
            }
        }
    else if( dwLen2 && lpwiWave->bDonePlaying )
        {
        // Set the appropriate silence value
        memset( lpWrite2,
                    lpwiWave->pwfx->wBitsPerSample == 8 ? 128 : 0,
                    dwLen2 );
        }

#ifdef	__TEST_SOUND__
	ASSERT( lpwdWaves[dwUser] == lpwiWave );
#endif

    lpwiWave->lpDSBStreamBuffer->Unlock( (LPVOID)lpWrite1, dwLen1,
                                      (LPVOID)lpWrite2, dwLen2 );

#ifdef	__TEST_SOUND__
	ASSERT( lpwdWaves[dwUser] == lpwiWave );
#endif

    lpwiWave->dwNextWriteOffset += lpwiWave->dwBufferSegSize;
    if( lpwiWave->dwNextWriteOffset >= lpwiWave->dwBufferSize )
        lpwiWave->dwNextWriteOffset -= lpwiWave->dwBufferSize;


#ifdef	__TEST_SOUND__
	ASSERT( lpwdWaves[dwUser] == lpwiWave );
#endif

	//OutputString( DS_nVolume, "\n" );
	// set volume
    if( lpwiWave->lpDSBStreamBuffer->SetVolume( DS_nVolume ) != DS_OK )
	{
		OutputDebugString( "DS dynamic error: set volume!\n" );
		//-----debug
#ifdef	_DS_LOG_
		FILE*fp=fopen( "DSTest.log","at" );
		fprintf(fp,"E" );
		fclose(fp);
#endif
		//-----debug
		goto END_TIME;
	}

	// set pan
    if( lpwiWave->lpDSBStreamBuffer->SetPan( DS_nPan ) != DS_OK )
	{
		OutputDebugString( "DS dynamic error: set pan!\n" );
		//-----debug
#ifdef	_DS_LOG_
		FILE*fp=fopen( "DSTest.log","at" );
		fprintf(fp,"F" );
		fclose(fp);
#endif
		//-----debug
		goto END_TIME;
	}

	// fade in or fade out
	if( DS_nFadeMode == DS_FADE_OUT )
	{
		if( lpwiWave->FadeOut() )	
			DS_nFadeMode = DS_FADE_NONE;	// end fade session
	}
	if( DS_nFadeMode == DS_FADE_IN  )
	{
		if( lpwiWave->FadeIn() ) 
			DS_nFadeMode = DS_FADE_NONE;	// end fade session
	}

END_TIME:

    bInTimer = FALSE;
	ReleaseMutex( hMutex );
}
//////////////////



//==========================================================================
//
// class WAVESTATIC
//
//==========================================================================
//////////////////
// used by class WAVESTATIC
// load sound buffer
// nIndex		:	ID in global wave array
// return value	:	0 if succeeded, otherwise error number,
int StreamBufferSetupS( int nIndex );

// release sound buffer
// nIndex	:	ID in global wave array
void StreamBufferReleaseS( int nIndex );

// set lpswWave by index in lpwsWaves
// nIndex	:	ID in global wave array
LPWAVESTATIC SetToGlobalS( int nIndex );

// constructor
WAVESTATIC::CStaticWave()
{
	nID = WAVE_OBJECT_NONE;							/* ID, if not equals to WAVE_OBJECT_NONE,
											the wave file is opened */
	//strFileName[65];				/* wave file name */
    bLoopFile = FALSE;				/* Should we loop playback? */
	bPlaying = FALSE;				/* if wave file is playing */
    lpDSBStreamBuffer = NULL;		/* Points to DirectSoundBuffer */
	cbSize = 0;
	pbData = NULL;
}

// destructor
WAVESTATIC::~CStaticWave()
{
	if( nID != WAVE_OBJECT_NONE )
	{
        PostMessage( DS_hwndGame, WM_DSSTREAM_DONE, (WPARAM)nID, (LPARAM)1 );
		Stop();
		OutputDebugString( "DS Warning: not stop playing sound before destroy sound buffer!\n" );
	}
}

// load sound buffer
// filename		:	wave file name
// return value	:	TRUE if succeeded
BOOL WAVESTATIC::Load( LPCTSTR filename )
{
	// register this sound buffer to global array
	BOOL bSuccess = FALSE;
	for( int i=0; i< WAVE_OBJECT_MAXS; i++ )
	{
		if( lpwsWaves[i] == NULL )
		{
			lpwsWaves[i] = this;
			nID = i;
			bSuccess = TRUE;
#ifdef	__TEST_SOUND__
			for( int x=0; x<WAVE_OBJECT_MAXS; x++ )
			{
				if( nID == x )	continue;
				if( !lpwsWaves[x] ) continue;
				ASSERT( this != lpwsWaves[x] );
			}
#endif
			break;
		}
	}
	if( !bSuccess )
	{
		return FALSE;
	}
	
	// load sound with a wave format file
	strcpy( strFileName, filename );
	if(StreamBufferSetupS( nID ) != 0 )
	{
		// Error opening the WAVE file so abort
		lpwsWaves[nID] = NULL;
		nID = WAVE_OBJECT_NONE;
		return FALSE;
	}
	nSWaveCounter++;
#ifdef	__TEST_SOUND__
	ASSERT( lpwsWaves[nID]->lpDSBStreamBuffer );
#endif

#ifdef	_DEBUG
	char	tmp[33];
	itoa( nSWaveCounter, tmp, 10 );
//	OutputDebugString( "DS Static wave Load:" );
//	OutputDebugString(tmp);
//	OutputDebugString("\n");
#endif

	return TRUE;
}

// release sound buffer
void WAVESTATIC::Close()
{
	if( nID == WAVE_OBJECT_NONE )	return;

//	WaitForSingleObject( hMutex, INFINITE );
	// release sound buffer, and unregister from global array
	StreamBufferReleaseS( nID );
	lpwsWaves[nID] = NULL;
	nID = WAVE_OBJECT_NONE;
	nSWaveCounter--;

#ifdef	_DEBUG
	char	tmp[33];
	itoa( nSWaveCounter, tmp, 10 );
//	ReleaseMutex( hMutex );
//	OutputDebugString( "DS Static wave Close:" );
//	OutputDebugString(tmp);
//	OutputDebugString("\n");
#endif
}

// dwFlags		:	TRUE if looping
// pos			:	position to play from
// return value	:	TRUE if played
// if this object is playing, stop and playing again
BOOL WAVESTATIC::Play( DWORD dwFlags/*=DSBPLAY_LOOPING*/, DWORD pos/*=0*/ )
{
	if( !DS_bSoundEnable )	return TRUE;

	// release stoped wave files previously played
	DS_CloseStopedSoundS();
	
	// stop if the current buffer is playing
	if( bPlaying == TRUE )
	{
        PostMessage( DS_hwndGame, WM_DSSTREAM_DONE, (WPARAM)nID, (LPARAM)1 );
		Stop();
	}

	if( !Load( strFileName ) )	
		return FALSE;

#ifdef	__TEST_SOUND__
		ASSERT( nID != WAVE_OBJECT_NONE );
		ASSERT( lpDSBStreamBuffer );
#endif

    // Ensure that position is at 0, ready to go
	// set position
    if( lpDSBStreamBuffer->SetCurrentPosition( pos ) != DS_OK )
		return FALSE;
	// set volume
    if( lpDSBStreamBuffer->SetVolume( DS_nVolume ) != DS_OK )
		return FALSE;
	// set pan
    if( lpDSBStreamBuffer->SetPan( DS_nPan ) != DS_OK )
		return FALSE;

	// play
    if( lpDSBStreamBuffer->Play( 0, 0, dwFlags ) != DS_OK )
		return FALSE;

	bPlaying = TRUE;
	bLoopFile = dwFlags;

	return TRUE;
}

// stop playing
// if has stoped, no effects
void WAVESTATIC::Stop()
{
    HRESULT dsrval;

//	WaitForSingleObject( hMutex, INFINITE );
#ifdef	__TEST_SOUND__
	ASSERT( bPlaying );
#endif

    if( bPlaying )
    {
        bPlaying = FALSE;
		dsrval = lpDSBStreamBuffer->Stop();
//		ReleaseMutex( hMutex );
		Close();
//		WaitForSingleObject( hMutex, INFINITE );
    }
//	ReleaseMutex( hMutex );
	return;
}

BOOL WAVESTATIC::IfPlaying()
{
	HRESULT	dsrval;
	DWORD dwStatus;
	if( lpDSBStreamBuffer == NULL )	return FALSE;
	dsrval = lpDSBStreamBuffer->GetStatus( &dwStatus );
	if ( DS_OK != dsrval ) return FALSE;
	if( !(dwStatus & DSBSTATUS_PLAYING) )
	{
		// must be sendmessage, cannot use postmessage
		// LPARAM	:	1 if it is static wave object
	    SendMessage( DS_hwndGame, WM_DSSTREAM_DONE, (WPARAM)nID, (LPARAM)1 );
		Stop();
		return FALSE;
	}
	return TRUE;
}

// nIndex		:	ID in global wave array
// return value	:	0 if succeeded, otherwise error number,
int StreamBufferSetupS( int nIndex )
{
	DWORD dwSamples;
    DSBUFFERDESC dsbd;
    HRESULT      dsRetVal;
    LPBYTE       lpWrite1, lpWrite2;
    DWORD        dwLen1, dwLen2;
	int nChkErr = 0;

	WAVESTATIC * lpswWave = NULL;
	lpswWave = SetToGlobalS( nIndex );

	// open the whole wave file, read data to pbData, and close it
	if( WaveLoadFile( lpswWave->strFileName, &lpswWave->cbSize, 
			&dwSamples, &lpswWave->pwfx, &lpswWave->pbData ) != 0 )
	{
        nChkErr =  ERR_WAVE_OPEN_FAILED;
	    //SoundFail( DS_hwndGame, nChkErr );
		goto END_SETUP;
	}

	// test if the format is correct
    if( lpswWave->pwfx->wFormatTag != WAVE_FORMAT_PCM )
    {
		nChkErr = ERR_WAVE_INVALID_FORMAT;
        //SoundFail( DS_hwndGame, nChkErr );
		goto END_SETUP;
    }

    /*
     * Create the secondary DirectSoundBuffer object to receive our sound data.
     */
    memset( &dsbd, 0, sizeof( DSBUFFERDESC ));
    dsbd.dwSize = sizeof( DSBUFFERDESC );
    dsbd.dwFlags = DSBCAPS_CTRLDEFAULT;
    dsbd.dwBufferBytes = lpswWave->cbSize;

#ifdef	__TEST_SOUND__
	ASSERT( lpwsWaves[nIndex] == lpswWave );
#endif

    /* Set Format properties according to the WAVE file we just opened */
	//LPDIRECTSOUNDBUFFER     lpDSB = NULL;
    dsbd.lpwfxFormat = lpswWave->pwfx;
    dsRetVal = lpDS->CreateSoundBuffer( &dsbd,
                                        &lpswWave->lpDSBStreamBuffer,
                                        NULL );
    if( dsRetVal != DS_OK )
    {
		nChkErr = ERR_CREATEDSB_FAILED;
        //SoundFail( DS_hwndGame, nChkErr );
		goto END_SETUP;
    }
/*
	// set wave format 
    if ((dsRetVal = lpDSBPrimary->SetFormat(&wfx)) != DS_OK)
	{
		nChkErr = ERR_WAVE_FORMAT_INVALID;
		SoundFail( DS_hwndGame, nChkErr );
		goto END_SETUP;
	}
*/
	// lock buffer and copy sound data to it
    dsRetVal = lpswWave->lpDSBStreamBuffer->Lock( 0, lpswWave->cbSize,
								               (void**)&lpWrite1, (&dwLen1),
											   (void**)&lpWrite2, (&dwLen2),
											   0 );
    if( dsRetVal != DS_OK )
    {
		nChkErr = ERR_CREATEDSB_LOST;
        //SoundFail( DS_hwndGame, nChkErr );
		lpswWave->lpDSBStreamBuffer->Release();
		lpswWave->lpDSBStreamBuffer = NULL;
		goto END_SETUP;
    }

	// copy data
	memcpy( lpWrite1, lpswWave->pbData, dwLen1 );
	memcpy( lpWrite2, lpswWave->pbData+dwLen1, dwLen2 );

	// unlock
    dsRetVal = lpswWave->lpDSBStreamBuffer->Unlock( (LPVOID)lpWrite1, dwLen1,
                                        (LPVOID)lpWrite2, 0 );

	// LPARAM	:	0 if it is dynamic wave object
    SendMessage( DS_hwndGame, WM_DSSTREAM_PROGRESS, 0L, 0L );
	nChkErr = 0;
END_SETUP:
	
	return nChkErr;
}

void StreamBufferReleaseS( int nIndex )
{
	WAVESTATIC * lpswWave = NULL;
	lpswWave = SetToGlobalS( nIndex );

	// release sound buffer, and unregister from global array
	// wave file has been closed in Load()
	// just free some data
	GlobalFree(lpswWave->pwfx);	lpswWave->pwfx = NULL;
	GlobalFree(lpswWave->pbData); lpswWave->pbData = NULL;

	if( lpswWave->lpDSBStreamBuffer )
		lpswWave->lpDSBStreamBuffer->Release();
	lpswWave->lpDSBStreamBuffer = NULL;
}

LPWAVESTATIC SetToGlobalS( int nIndex )
{
	return lpwsWaves[nIndex];
}

// for static wave only
// called if the static sound buffer got the end
// clear all the sounds played prviously, and now they are stoped.
void DS_CloseStopedSoundS()
{
	HRESULT	dsrval;
	// close static sound buffers that previously opened.
	for( int i=0; i<WAVE_OBJECT_MAXS; i++ )
	{
		if( lpwsWaves[i] != NULL )
		{	
			DWORD dwStatus;
#ifdef	__TEST_SOUND__
			ASSERT( lpwsWaves[i]->nID != WAVE_OBJECT_NONE );
#endif
			dsrval = lpwsWaves[i]->lpDSBStreamBuffer->GetStatus( &dwStatus );
		    if ( DS_OK != dsrval ) continue;
			if( !(dwStatus & DSBSTATUS_PLAYING) )
			{
				// must be sendmessage, cannot use postmessage
				// LPARAM	:	1 if it is static wave object
	            SendMessage( DS_hwndGame, WM_DSSTREAM_DONE, (WPARAM)lpwsWaves[i]->nID, (LPARAM)1 );
				lpwsWaves[i]->Stop();
			}
		}
	}
}
//////////////////
